"""
AIDevFlow 管理控制台 (Flask)

用途
- 提供可视化界面以维护/修改框架：规则与配置编辑、脚本运行、初始化。

路由
- `/`：仪表盘，展示入口（单一框架模式）
- `/rules`：列出规则文件（rules/*.md）并跳转到文件编辑
- `/edit`：编辑指定相对路径文件（GET 显示，POST 保存）
- `/config`：编辑 `config/template_gen_rules.json`，含 JSON 校验
- `/run`：运行 Python CLI 常用命令（gen-templates/verify/apply），可附加参数
- `/init`：根据语言与风格进行初始化（调用 CLI 入口）

注意
- 此界面封装高频操作，未覆盖全部 CLI 功能；低风险路径校验用于编辑任意相对路径但确保在仓库内。
- 为了方便预览，主程序启动会打印预览地址，如 `http://localhost:8000/`。
"""

from __future__ import annotations

import argparse
import json
import os
import sys
import subprocess
from pathlib import Path
from flask import Flask, request, render_template, redirect, url_for

BASE = Path(__file__).resolve().parents[2]
APP = Flask(
    __name__,
    template_folder=str(Path(__file__).parent / 'templates'),
    static_folder=str(Path(__file__).parent / 'static')
)

REPOS = ['AIDevFlow']
ADMIN_REPO = os.environ.get('ADMIN_REPO', 'AIDevFlow')
if ADMIN_REPO not in REPOS:
    ADMIN_REPO = 'AIDevFlow'

# 控制台标题（可配置，默认 AIDevFlow 管理控制台）
ADMIN_TITLE = os.environ.get('ADMIN_TITLE', 'AIDevFlow 管理控制台')

def repo_path() -> Path:
    """
    解析当前被管理的框架仓库路径。
    - 当 ADMIN_REPO 与 BASE 名称相同（自我管理场景），直接返回 BASE。
    - 否则尝试定位同级目录中的指定仓库名称；若不存在则回退到 BASE。
    """
    try:
        if ADMIN_REPO == BASE.name:
            return BASE
        sibling = BASE.parent / ADMIN_REPO
        return sibling if sibling.exists() else BASE
    except Exception:
        return BASE

def inside_repo(target: Path, repo: Path) -> bool:
    try:
        return repo.resolve() in target.resolve().parents or target.resolve() == repo.resolve()
    except Exception:
        return False

@APP.route('/', methods=['GET'])
def index():
    return render_template('dashboard.html', repo_name=ADMIN_REPO, site_title=ADMIN_TITLE)

## 单一框架模式：不提供仓库选择路由

@APP.route('/rules', methods=['GET'])
def list_rules():
    rpath = repo_path()
    files = list((rpath / 'rules').glob('*.md'))
    return render_template('rules.html', files=files, repo_name=ADMIN_REPO, repo=rpath, site_title=ADMIN_TITLE)

@APP.route('/edit', methods=['GET', 'POST'])
def edit_file():
    repo = repo_path()
    rel = request.args.get('path') or request.form.get('path')
    if not rel:
        return redirect('/rules')
    target = (repo / rel).resolve()
    if not inside_repo(target, repo):
        return ('非法路径', 400)
    if request.method == 'POST':
        content = request.form.get('content', '')
        target.parent.mkdir(parents=True, exist_ok=True)
        target.write_text(content, encoding='utf-8')
        return redirect(f'/edit?path={rel}')
    content = target.read_text(encoding='utf-8', errors='ignore') if target.exists() else ''
    return render_template('edit_file.html', path=rel, content=content, repo_name=ADMIN_REPO, site_title=ADMIN_TITLE)

@APP.route('/config', methods=['GET'])
def list_config():
    rpath = repo_path()
    files = list((rpath / 'config').glob('*'))
    return render_template('config_index.html', files=files, repo_name=ADMIN_REPO, repo=rpath, site_title=ADMIN_TITLE)

@APP.route('/config/edit', methods=['GET', 'POST'])
def edit_config_file():
    rpath = repo_path()
    rel = request.args.get('path') or request.form.get('path')
    if not rel:
        return redirect('/config')
    f = (rpath / rel).resolve()
    if not inside_repo(f, rpath):
        return ('非法路径', 400)
    error = None
    if request.method == 'POST':
        raw = request.form.get('content', '')
        if f.name == 'template_gen_rules.json':
            try:
                parsed = json.loads(raw) if raw.strip() else {}
                f.parent.mkdir(parents=True, exist_ok=True)
                f.write_text(json.dumps(parsed, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
            except Exception as e:
                error = f'JSON 解析失败: {e}'
        else:
            f.parent.mkdir(parents=True, exist_ok=True)
            f.write_text(raw, encoding='utf-8')
        if not error:
            return redirect(f'/config/edit?path={rel}')
    content = f.read_text(encoding='utf-8', errors='ignore') if f.exists() else ''
    return render_template('edit_file.html', path=rel, content=content, repo_name=ADMIN_REPO, error=error, site_title=ADMIN_TITLE)

@APP.route('/run', methods=['GET', 'POST'])
def run_cmd():
    repo = repo_path()
    output = None
    if request.method == 'POST':
        cmd = request.form.get('cmd', 'gen-templates')
        args = request.form.get('args', '')
        WHITELIST = {'gen-templates', 'gen-templates-by-rules', 'verify', 'apply', 'setup-framework-mode'}
        if cmd not in WHITELIST:
            output = f'不支持的命令: {cmd}'
            return render_template('run.html', output=output, repo_name=ADMIN_REPO, site_title=ADMIN_TITLE)
        py = [sys.executable, str(repo / 'scripts' / 'cli.py'), cmd]
        if args:
            py += args.split()
        try:
            proc = subprocess.run(
                py, cwd=str(repo), capture_output=True, text=True,
                env={**os.environ, 'PAGER': 'cat'}
            )
            output = proc.stdout + ("\n" + proc.stderr if proc.stderr else "")
        except Exception as e:
            output = f'执行失败: {e}'
    return render_template('run.html', output=output, repo_name=ADMIN_REPO, site_title=ADMIN_TITLE)

@APP.route('/init', methods=['GET', 'POST'])
def init_repo():
    repo = repo_path()
    output = None

    def update_env(env_path: Path, updates: dict[str, str]) -> None:
        existing = {}
        if env_path.exists():
            try:
                for line in env_path.read_text(encoding='utf-8', errors='ignore').splitlines():
                    if '=' in line and not line.strip().startswith('#'):
                        k, v = line.split('=', 1)
                        existing[k.strip()] = v.strip()
            except Exception:
                existing = {}
        existing.update(updates or {})
        env_path.parent.mkdir(parents=True, exist_ok=True)
        lines = [f"{k}={v}" for k, v in existing.items()]
        env_path.write_text("\n".join(lines) + "\n", encoding='utf-8')

    if request.method == 'POST':
        project = request.form.get('project') or Path(BASE).name
        langs_sel = request.form.getlist('langs')
        langs_text = request.form.get('langs', '')
        langs = langs_sel if langs_sel else ([x.strip() for x in langs_text.split(',') if x.strip()] or ['python'])
        style = request.form.get('style', 'mimic')
        user = request.form.get('user', 'dev')
        evolve = request.form.get('evolve', 'none')  # none|verify|apply
        no_config = request.form.get('no_config') == 'on'
        write_config = request.form.get('write_config') == 'on'

        # 写入配置（除非选择 no-config）
        if not no_config and write_config:
            env_updates = {
                'PROJECT_NAME': project,
                'LANGUAGES': ' '.join(langs),
                'ARCHITECTURE_STYLE': style,
                'STYLE_JAVA': style,
                'STYLE_PYTHON': style,
                'STYLE_NODE': style,
                'FRAMEWORK_STATUS': 'paired',
            }
            env_file = repo / 'config' / 'rules.env'
            update_env(env_file, env_updates)
            output = (output or '') + f"写入配置: {env_file}\n"

        # 生成开发模板（非破坏性）
        for lang in langs:
            try:
                proc = subprocess.run(
                    [sys.executable, str(repo / 'scripts' / 'cli.py'), 'dev-templates', lang, style],
                    cwd=str(repo), capture_output=True, text=True,
                    env={**os.environ, 'PAGER': 'cat'}
                )
                output = (output or '') + proc.stdout + ("\n" + proc.stderr if proc.stderr else "")
            except Exception as e:
                output = (output or '') + f"[dev-templates] 执行失败({lang}): {e}\n"

        # 触发框架演进（verify/apply）
        if evolve in ('verify', 'apply'):
            try:
                proc = subprocess.run(
                    [sys.executable, str(repo / 'scripts' / 'cli.py'), 'framework-evolve', evolve],
                    cwd=str(repo), capture_output=True, text=True,
                    env={**os.environ, 'PAGER': 'cat'}
                )
                output = (output or '') + proc.stdout + ("\n" + proc.stderr if proc.stderr else "")
            except Exception as e:
                output = (output or '') + f"[framework-evolve] 执行失败: {e}\n"

    return render_template('init.html', output=output, repo_name=ADMIN_REPO, site_title=ADMIN_TITLE)

@APP.route('/mapping', methods=['GET'])
def view_mapping():
    rpath = repo_path()
    doc = rpath / 'docs' / 'scripts_python_mapping.md'
    title = 'scripts_python_mapping.md'
    content = doc.read_text(encoding='utf-8', errors='ignore') if doc.exists() else '未找到映射文档。'
    return render_template('doc_view.html', title=title, content=content, repo_name=ADMIN_REPO, site_title=ADMIN_TITLE)

@APP.route('/search', methods=['GET'])
def search_rules():
    rpath = repo_path()
    q = request.args.get('q', '').strip()
    results = []
    if q:
        for md in (rpath / 'rules').glob('*.md'):
            try:
                lines = md.read_text(encoding='utf-8', errors='ignore').splitlines()
                for i, line in enumerate(lines, 1):
                    if q in line:
                        results.append({'file': str(md.relative_to(rpath)), 'line': i, 'text': line[:200]})
            except Exception:
                pass
    return render_template('search.html', q=q, results=results, repo_name=ADMIN_REPO, site_title=ADMIN_TITLE)

@APP.route('/framework_mode', methods=['GET', 'POST'])
def framework_mode():
    repo = repo_path()
    output = None
    mode = None
    remote = None
    branch = 'main'
    applied = False

    def update_env(env_path: Path, updates: dict[str, str]) -> None:
        existing = {}
        if env_path.exists():
            try:
                for line in env_path.read_text(encoding='utf-8', errors='ignore').splitlines():
                    if '=' in line and not line.strip().startswith('#'):
                        k, v = line.split('=', 1)
                        existing[k.strip()] = v.strip()
            except Exception:
                existing = {}
        existing.update(updates or {})
        env_path.parent.mkdir(parents=True, exist_ok=True)
        lines = [f"{k}={v}" for k, v in existing.items()]
        env_path.write_text("\n".join(lines) + "\n", encoding='utf-8')

    if request.method == 'POST':
        mode = (request.form.get('mode') or '').strip()
        remote = (request.form.get('remote') or '').strip()
        branch = (request.form.get('branch') or 'main').strip()
        apply_flag = request.form.get('apply') == 'on'

        if mode not in ('submodule', 'subtree', 'vendor', 'ignored'):
            output = f"[ERROR] --mode 必填: submodule|subtree|vendor|ignored"
        else:
            lines = []
            if mode == 'submodule':
                lines += [
                    "[建议步骤] Submodule 模式",
                    f"1) 在项目根执行：git submodule add {remote or '<AIDevFlow-remote-url>'} AIDevFlow",
                    "2) 初始化子模块：git submodule update --init --recursive",
                    "3) 后续更新：git submodule update --remote --merge",
                ]
            elif mode == 'subtree':
                lines += [
                    "[建议步骤] Subtree 模式",
                    f"1) 引入：git subtree add --prefix=AIDevFlow {remote or '<AIDevFlow-remote-url>'} {branch} --squash",
                    f"2) 更新：git subtree pull --prefix=AIDevFlow {remote or '<AIDevFlow-remote-url>'} {branch} --squash",
                    "3) 回推：git subtree push --prefix=AIDevFlow feature-x",
                ]
            elif mode == 'vendor':
                lines += [
                    "[建议步骤] Vendor 模式",
                    "1) 将 AIDevFlow 拷贝为普通目录，确保无 AIDevFlow/.git 子目录",
                    "2) 直接在主项目中提交与维护该目录（vendor 语义）",
                ]
                lines += [
                    "[提示] 如果不希望随主项目提交，请选择 ignored 模式：bash scripts/setup-framework-mode.sh --mode=ignored --apply",
                ]
            elif mode == 'ignored':
                lines += [
                    "[建议步骤] Ignore 模式",
                    "1) 在项目根 .gitignore 中添加 'AIDevFlow/'",
                ]
                if apply_flag:
                    gitignore = BASE / '.gitignore'
                    try:
                        existed = gitignore.exists() and ('AIDevFlow/' in gitignore.read_text(encoding='utf-8', errors='ignore'))
                        if existed:
                            lines.append("[已存在] .gitignore 中已包含 'AIDevFlow/'")
                        else:
                            with gitignore.open('a', encoding='utf-8') as fp:
                                fp.write("AIDevFlow/\n")
                            lines.append("[应用] 已写入 .gitignore: AIDevFlow/")
                        applied = True
                    except Exception as e:
                        lines.append(f"[ERROR] 写入 .gitignore 失败: {e}")

            # 持久化模式到 rules.env
            env_file = repo / 'config' / 'rules.env'
            update_env(env_file, {'FRAMEWORK_DISTRIBUTION_MODE': mode})
            lines.append(f"[已记录] FRAMEWORK_DISTRIBUTION_MODE={mode} → {env_file}")
            output = "\n".join(lines)

    return render_template('framework_mode.html', output=output, repo_name=ADMIN_REPO, site_title=ADMIN_TITLE, mode=mode or '', remote=remote or '', branch=branch or 'main', applied=applied)

def create_app():
    return APP

def main():
    parser = argparse.ArgumentParser(description='AIDevFlow 管理控制台')
    parser.add_argument('--port', type=int, default=8000)
    args = parser.parse_args()
    url = f'http://localhost:{args.port}/'
    print(f'预览地址: {url}')
    APP.run(host='0.0.0.0', port=args.port, debug=True)

if __name__ == '__main__':
    main()
